#include "stdafx.h"
#include "Graphics.h"

#include "GlobalAppData.h"
#include "GraphicsResources.h"
#include "Renderer.h"
#include "EditorRenderStage.h"
#include "DebugDraw.h"

#include "Text.h"

#include "2DUtils.h"

using namespace mgfx;

Graphics* Graphics::Instance = NULL;

Graphics::Graphics()
{
	mhMainWnd   = 0;

	mDevice          = 0;
	mSwapChain          = 0;
	mDepthStencilBuffer = 0;
	mRenderTargetView   = 0;
	mDepthStencilView   = 0;
	//mFont               = 0;

	mDriverType = D3D_DRIVER_TYPE_HARDWARE;
	mFeatureLevel = D3D_FEATURE_LEVEL_11_0;

	mClearColor     = XMCOLOR4F(0.0f, 0.0f, 0.0f, 1.0f);
	mClientWidth    = 800; // default;
	mClientHeight   = 600;

	Instance = this;
}

Graphics::~Graphics()
{
	TwDeleteAllBars();
	TwTerminate();

	if( mDeviceContext )
		mDeviceContext->ClearState();

	delete Renderer::Instance;
	Text2D::deinit();
	Utils2D::deinit();
	delete mGfxResources;

	ReleaseCOM(mRenderTargetView);
	ReleaseCOM(mDepthStencilView);
	ReleaseCOM(mSwapChain);
	ReleaseCOM(mDepthStencilBuffer);
	ReleaseCOM(mDeviceContext);
	ReleaseCOM(mDevice);
	//ReleaseCOM(mFont);
}

void Graphics::resetOMTargetsAndViewport()
{
	mDeviceContext->OMSetRenderTargets(1, &mRenderTargetView, NULL);

	D3D11_VIEWPORT vp;
	vp.TopLeftX = 0.0f;
	vp.TopLeftY = 0.0f;
	vp.Width    = (float)mClientWidth;
	vp.Height   = (float)mClientHeight;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;

	mDeviceContext->RSSetViewports(1, &vp);
}

void Graphics::init(HWND hwnd, int clientWidth, int clientHeight)
{
	mhMainWnd = hwnd;
	mClientWidth = clientWidth;
	mClientHeight = clientHeight;

	mcore::GlobalAppData::Instance.ScreenWidth = clientWidth;
	mcore::GlobalAppData::Instance.ScreenHeight = clientHeight;
	mcore::GlobalAppData::Instance.MSAASamples = 1;

	initDirect3D();

	Renderer::Instance = new Renderer(mDevice, mDeviceContext, mRenderTargetView, mDepthStencilView);
	mGfxResources = new GraphicsResources();
	Utils2D::init(mDevice, mDeviceContext);
	Text2D::init(mDevice, mDeviceContext);

	Renderer::Instance->init();

	mgfx::initEditorRenderStage();
	mgfx::debug::init();

	//mRenderer.init(mDevice);
}

void Graphics::initDirect3D()
{
	// Fill out a DXGI_SWAP_CHAIN_DESC to describe our swap chain.

	DXGI_SWAP_CHAIN_DESC sd;
	sd.BufferDesc.Width  = mClientWidth;
	sd.BufferDesc.Height = mClientHeight;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

	// No multisampling.
	sd.SampleDesc.Count   = 1;
	sd.SampleDesc.Quality = 0;

	sd.BufferUsage  = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.BufferCount  = 1;
	sd.OutputWindow = mhMainWnd;
	sd.Windowed     = true;
	sd.SwapEffect   = DXGI_SWAP_EFFECT_DISCARD;
	sd.Flags        = 0;


	// Create the device.

	UINT createDeviceFlags = 0;
	createDeviceFlags |= D3D10_CREATE_DEVICE_SINGLETHREADED;
#if defined(DEBUG) || defined(_DEBUG)  
	createDeviceFlags |= D3D10_CREATE_DEVICE_DEBUG;
#endif

	D3D_FEATURE_LEVEL tmpLevel;
	HR( D3D11CreateDeviceAndSwapChain(
			0,                 //default adapter
			mDriverType,
			0,                 // no software device
			createDeviceFlags,
			&mFeatureLevel,
			1,
			D3D11_SDK_VERSION,
			&sd,
			&mSwapChain,
			&mDevice,
			&tmpLevel,
			&mDeviceContext) );

	//AppData::Instance.Device = mDevice;
	//AppData::Instance.ScreenWidth = mClientWidth;
	//AppData::Instance.ScreenHeight = mClientHeight;

	// The remaining steps that need to be carried out for d3d creation
	// also need to be executed every time the window is resized.  So
	// just call the onResize method here to avoid code duplication.
	
	onResize();

	TwInit(TW_DIRECT3D11, mDevice);
	TwWindowSize(mClientWidth, mClientHeight);
}



void Graphics::onResize()
{
	// Release the old views, as they hold references to the buffers we
	// will be destroying.  Also release the old depth/stencil buffer.

	ReleaseCOM(mRenderTargetView);
	ReleaseCOM(mDepthStencilView);
	ReleaseCOM(mDepthStencilBuffer);


	// Resize the swap chain and recreate the render target view.

	HR(mSwapChain->ResizeBuffers(1, mClientWidth, mClientHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
	ID3D11Texture2D* backBuffer;
	HR(mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBuffer)));
	HR(mDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTargetView));
	ReleaseCOM(backBuffer);


	// Create the depth/stencil buffer and view.

	D3D11_TEXTURE2D_DESC depthStencilDesc;
	
	depthStencilDesc.Width     = mClientWidth;
	depthStencilDesc.Height    = mClientHeight;
	depthStencilDesc.MipLevels = 1;
	depthStencilDesc.ArraySize = 1;
	depthStencilDesc.Format    = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthStencilDesc.SampleDesc.Count   = mcore::GlobalAppData::Instance.MSAASamples;
	depthStencilDesc.SampleDesc.Quality = 0;//D3D11_STANDARD_MULTISAMPLE_PATTERN;
	depthStencilDesc.Usage          = D3D11_USAGE_DEFAULT;
	depthStencilDesc.BindFlags      = D3D11_BIND_DEPTH_STENCIL;
	depthStencilDesc.CPUAccessFlags = 0; 
	depthStencilDesc.MiscFlags      = 0;

	HR(mDevice->CreateTexture2D(&depthStencilDesc, 0, &mDepthStencilBuffer));
	HR(mDevice->CreateDepthStencilView(mDepthStencilBuffer, 0, &mDepthStencilView));

	resetOMTargetsAndViewport();
}

void Graphics::updateTimer(float dt)
{
	static int frameCnt = 0;
	static float t_base = 0.0f;
	static float t_current = 0.0f;

	frameCnt++;
	t_current += dt;

	if( (t_current - t_base) >= 1.0f )
	{
		float fps = (float)frameCnt; // fps = frameCnt / 1
		float mspf = 1000.0f / fps;

		std::stringstream outs;   
		outs.precision(6);

		outs << "FPS: " << fps;
		mFpsText = outs.str();
		LOG(outs.str());

		outs.str("");
		outs << "Ms per frame: " << mspf;
		mFrameTimeText = outs.str();
		LOG(outs.str());
		
		// Reset for next average.
		frameCnt = 0;
		t_base  += 1.0f;
	}
}

void Graphics::preDraw()
{
	mDeviceContext->ClearRenderTargetView(mRenderTargetView, (float*)&mClearColor);
	mDeviceContext->ClearDepthStencilView(mDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
}

void Graphics::postDraw()
{
	Text2D::draw(mFpsText, 0, 0, 1.2f);
	Text2D::draw(mFrameTimeText, 0, 20, 1.2f);

	TwDraw();

	mSwapChain->Present(0, 0);
}